package com.jjzhk.common.beanConfig;

import com.jjzhk.common.bean.RpcRequest;
import com.jjzhk.common.bean.RpcResponse;
import com.jjzhk.common.handler.RpcClientHandler;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.*;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * CallerBean
 *
 * @author : JJZHK
 * @date : 2016-08-15
 * @comments :
 **/
public class CallerBean implements FactoryBean,
        DisposableBean, ApplicationContextAware,BeanPostProcessor{
    private Logger logger = LoggerFactory.getLogger(CallerBean.class);
    private String interfaces;
    private Class<?> proxyClass;
    private String ref;
    private String SerializationClass;

    public String getSerializationClass() {
        return SerializationClass;
    }

    public void setSerializationClass(String serializationClass) {
        SerializationClass = serializationClass;
    }

    private int interval;
    private int trycount;

    private RegistryBean zookeeper;

    private String host;
    private int port;
    private ApplicationContext ctx;

    public String getInterfaces() {
        return interfaces;
    }

    public void setInterfaces(String interfaces) {
        this.interfaces = interfaces;
    }

    public String getRef() {
        return ref;
    }

    public void setRef(String ref) {
        this.ref = ref;
    }

    public int getInterval() {
        return interval;
    }

    public void setInterval(int interval) {
        this.interval = interval;
    }

    public int getTrycount() {
        return trycount;
    }

    public void setTrycount(int trycount) {
        this.trycount = trycount;
    }

    @Override
    public void destroy() throws Exception {

    }

    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        if (o instanceof RegistryBean)
        {
            zookeeper = (RegistryBean) o;
        }
        return o;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }

    @Override
    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(proxyClass.getClassLoader(), new Class<?>[]{proxyClass},
                (proxy, method, args) -> {
                    RpcRequest request = new RpcRequest();
                    request.setRequestID(UUID.randomUUID().toString());
                    request.setInterfaceName(method.getDeclaringClass().getName());
                    request.setServiceVersion(ref);
                    request.setMethodName(method.getName());
                    request.setParameterTypes(method.getParameterTypes());
                    request.setParameterValues(args);

                    long time = System.currentTimeMillis();

                    RpcResponse response = null;
                    int count = 1;

                    while(count <= trycount)
                    {
                        try {
                            String address = zookeeper.discoverService(method.getDeclaringClass().getName() + "-" + ref);
                            String[] addArgs = StringUtils.split(address, ":");

                            response = new RpcClientHandler(addArgs[0],Integer.parseInt(addArgs[1]), SerializationClass).send(request);
                            logger.info("time: {}ms", System.currentTimeMillis() - time);
                            break;
                        }
                        catch (Exception ex)
                        {
                            logger.error("try ... {}", count);
                            count++;
                            Thread.sleep(interval);
                        }
                    }

                    if (response == null || response.hasException()) {
                        return null;
                    }
                    else {
                        return response.getResult();
                    }
                });
    }

    @Override
    public Class<?> getObjectType() {
        try {
            proxyClass = Class.forName(this.interfaces);
        }
        catch (Exception ex)
        {
            logger.error("get bean class error : {}", ex);
        }

        return proxyClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
